package com.region.plugin.proxy.cglib;

import com.region.common.utils.ArrayUtils;
import com.region.common.utils.ClassUtils;
import com.region.plugin.Interceptor;
import com.region.plugin.proxy.ProxyHandler;
import net.sf.cglib.proxy.Enhancer;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * CGLIB Proxy Processor
 *
 * @author liujieyu
 * @date 2023/5/23 23:45
 * @desciption
 */
public class CglibProxyHandler implements ProxyHandler {

    // save the class of target object
    private ThreadLocal<Class<?>> superClassLocal = new ThreadLocal<Class<?>>() {
        @Override
        protected Class<?> initialValue() {
            return null;
        }
    };

    @Override
    public Object createProxyInstance(Object target, Class<?>[] classes, Interceptor interceptor, Object[] args) throws UnsupportedOperationException {
        if (interceptor == null) {
            return target;
        }
        if (superClassLocal.get() == null) {
            superClassLocal.set(target.getClass());
        }
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(superClassLocal.get());
        enhancer.setInterfaces(classes);
        enhancer.setCallback(new CglibRealProxy(target, interceptor));
        return getProxyObject(enhancer, args);
    }

    @Override
    public void completeProxy(Object target, List<Interceptor> interceptors, Object[] args) {
        this.superClassLocal.remove();
    }

    /**
     * Get proxy object
     *
     * @param enhancer
     * @param args
     * @return
     */
    private Object getProxyObject(Enhancer enhancer, Object[] args) {
        if (args == null || args.length == 0) {
            return enhancer.create();
        } else {
            List<List> allClassList = new ArrayList<>(args.length);
            // Get size of all parameter implements interface
            for (int i = 0; i < args.length; i++) {
                allClassList.add(Arrays.asList(ClassUtils.getAllInterfaces(args[i].getClass())));
            }
            List<List> descartes = ArrayUtils.descartes(allClassList);
            for (List clazzList : descartes) {
                try {
                    return enhancer.create((Class[]) clazzList.toArray(new Class[0]), args);
                } catch (Exception e) {
                    // no print exception
                }
            }
            return null;
        }
    }

}
